﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Interface;
using PServiceBus.Core.Manager;
using ThreadTask = System.Threading.Tasks.Task;
using PServiceBus.Core.Runtime;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Configuration;
using PServiceBus.Core.Logger;
using PServiceBus.Core.Runtime.Configuration;
using System.Diagnostics;

namespace PServiceBus.Core.Components {
    public class MessageBusHost : ComponentBase {
        #region IComponent Members

        public override void Close() {
            StopObservers();
        }

        public override void Init() {
            TopicManager.Current.Init(ConfigurationProvider.ComponentContainerConfig.Address, ConfigurationProvider.ComponentContainerConfig.AddressID);
            RegisterObservers();
            _perfCounter.Load(ConfigurationProvider.ComponentConfig.Name, new Dictionary<string, System.Diagnostics.PerformanceCounterType>() { 
               {TOTAL_PUBLISHED_WORKERS, PerformanceCounterType.NumberOfItems64},
               {TOTAL_AVAILABLE_WORKERS, PerformanceCounterType.NumberOfItems64}
            });
        }

       
        IEnumerable<IObserver> SubscriberObservers {
            get { return ObserverManager.GetObservers<ISubscriberObserver>(); }
        }

        IEnumerable<IObserver> TopicObservers {
            get { return ObserverManager.GetObservers<ITopicObserver>(); }
        }

        private void RegisterTopicObservers() {
            foreach (var observer in TopicObservers) {
                var topicObserver = observer as ITopicObserver;
                topicObserver.Manager = WorkerManager.Current;
                topicObserver.Init();
                topicObserver.Start();
            }
        }

        private void RegisterSubscriberObservers() {
            foreach (var observer in SubscriberObservers) {
                observer.Init();
                observer.Start();
            }
        }

        private void RegisterObservers() {
            RegisterTopicObservers();
            RegisterSubscriberObservers();
        }

        private void StopObservers() {
            StopTopicObservers();
            StopSubscriberObservers();
        }

        private void StopSubscriberObservers() {
            foreach (var observer in SubscriberObservers)
                observer.Stop();
        }


        private void StopTopicObservers() {
            foreach (var observer in TopicObservers)
                observer.Stop();
        }

        const string TOTAL_PUBLISHED_WORKERS = "TotalPublishedWorkers";
        const string TOTAL_AVAILABLE_WORKERS = "TotalAvailableWorkers";

        private long _publishedWorkerCount = 0;
        private PerfCounter _perfCounter = new PerfCounter();

        public override void Execute() {
            var tasks = new List<ThreadTask>();
            var workers = WorkerManager.Current.Workers;
            foreach (var worker in workers) {
                tasks.Add(ThreadTask.Factory.StartNew(o =>
                {
                    try {
                        var topic = o as IWorker;
                        var lastRunDate = topic.ExecutionDate ?? DateTime.Now;
                        var canRun = (topic.RunInterval == null ||
                            (topic.RunInterval != null &&
                            (topic.ExecutionDate == null || DateTime.Now.Subtract(lastRunDate).TotalDays >= topic.RunInterval.TotalDays))
                            );
                        if (canRun){
                            topic.Execute();
                            if (topic.Context != null && topic.Context.Count > 0) {
                                topic.ExecutionDate = DateTime.Now;
                                TopicMessageManager.Add(topic as ITopic, topic.Context);
                                topic.Context.Clear();
                                _publishedWorkerCount++;
                            }
                        }
                    }
                    catch (Exception ex) {
                        ESBLogger.Log(ex);
                    }
                }, worker));
            }
            if (tasks.Any())
                MethodHelper.TryLog(() => ThreadTask.WaitAll(tasks.ToArray()));
            _perfCounter.SetValue(TOTAL_PUBLISHED_WORKERS, _publishedWorkerCount);
            _perfCounter.SetValue(TOTAL_AVAILABLE_WORKERS, workers.Count());
        }

        #endregion
    }
}
